package com.excel.validators.core.extension;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.excel.validators.core.decorative.BasicClass;
import com.excel.validators.core.decorative.FieldElement;
import com.excel.validators.core.decorative.Validator;
import com.excel.validators.core.extension.annotation.AuditMetaData;
import com.excel.validators.core.extension.annotation.BaseNode;
import com.excel.validators.core.extension.annotation.DateNode;
import com.excel.validators.core.extension.annotation.DecimalNode;
import com.excel.validators.core.extension.annotation.InnerModel;
import com.excel.validators.core.extension.annotation.InnerSummary;
import com.excel.validators.log.Log;
import com.excel.validators.log.LogFactory;

/**
 * @author wangxiaoliang10
 *
 * 2017年8月5日
 */
@SuppressWarnings("rawtypes")
public final class ModelAnalysis {

		protected final Log logger = LogFactory.getLog(getClass());
		
		/**
		 * 定义的成员变量
		 */
		private FieldElement[] fields;
		
		/**
		 * 封装excel的模型类路径 
		 */
		private Class modelClass;
		
		/**
		 * 模型校验器
		 */
		private Validator validator;
		
		/**
		 * 以模型name为纬度
		 */
		private Map<String,FieldElement> fieldEntry;
		
		
		public ModelAnalysis(ClassLoader loader, String namespace) {
				init(loader,namespace);
		}

		public ModelAnalysis(ClassLoader loader ,InnerModel excelNode) {
				try {
						initExcelModel(loader,excelNode);
						initClassField();
				} catch (Exception e) {
						logger.error("class analysis faild,"
								+ " could not be found specified model class , "
								+ "model namespace: " + excelNode.namespace(),e);
						throw new IllegalArgumentException(e.getMessage());
				}
		}
		
		/**
		 * 初始化元数据，建立索引
		 * @param loader 
		 * @param namespace 
		 */
		private void init(ClassLoader loader, String namespace) {
				try {
						initAuditModel(loader,namespace);
						initClassField();
				} catch (Exception e) {
						logger.info("class analysis faild," 
								+ " model namespace: " + namespace
								+ ", program will be load Model Class...");
						throw new IllegalArgumentException(e.getMessage());
				}
		}
	
		/**
		 * 获取审计模型
		 * @param loader 
		 * @param namespace 
		 * @throws ClassNotFoundException 
		 * @throws IllegalAccessException 
		 * @throws InstantiationException 
		 */
		protected final void initAuditModel(ClassLoader loader, String namespace) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
				// TODO 获取注解,getAnnotationsByType 1.8以下不兼容
				Annotation[] annotations = ModelAnalysis.class
							.getAnnotations();
				
				for (Annotation annotation : annotations) {
						if (annotation instanceof InnerSummary) {
								InnerSummary summary = (InnerSummary) annotation;
								InnerModel[] models = summary.excelModels();
								// 分解models
								for (InnerModel model : models) {
										if (equals(model.namespace(), namespace)) {
												initExcelModel(loader, model);
										}
								}
						}
				}
		}
	
		protected void initExcelModel(ClassLoader loader, InnerModel model) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
				// initialization metadata
				this.modelClass = loader.loadClass(model.clspath());
				// If validator is none, ignore the validator 
				String validator = model.validator();
				if (validator != null && validator.length() != 0)
						this.validator = (Validator) loader.
								loadClass(model.validator()).newInstance();
				// metadata analysis
				AuditMetaData[] metadata = model.auditMetaDatas();
				// TODO 分析字段
				List<FieldElement> fields = new LinkedList<FieldElement>();
				for (AuditMetaData data : metadata) {
						FieldElement field = fieldAnalysis(data);
						fields.add(field);
				}
				this.fields = fields.toArray(new FieldElement[fields.size()]);
		}
		
		private void initClassField() {
				Field[] reflects = modelClass.getDeclaredFields();
				fieldEntry = new LinkedHashMap<String,FieldElement>(fields.length);
				for (FieldElement fieldElement : fields) {
						fieldEntry.put(fieldElement.getFieldName(), fieldElement);
				}
				for (Field reflect : reflects) {
							String fieldName = reflect.getName();
							FieldElement fieldElement = fieldEntry.get(fieldName);
							if (fieldElement != null) {
									reflect.setAccessible(true);
									fieldElement.setReflect(reflect);
							}
				}
		}
	
		/**
		 * 分析审计对象模型
		 * @param loader 
		 * @return 
		 */
		private FieldElement fieldAnalysis(AuditMetaData medadata) {
				int fieldIndex = medadata.index();
				String fieldName = medadata.name();
				String columnName = medadata.column();
				BaseNode baseNode = medadata.base();
				int clsType = baseNode.type();
				String pattern = null;
				if (clsType == BasicClass.DATE) {
						DateNode dateNode = baseNode.dateNode();
						if (dateNode != null)
								pattern = dateNode.pattern();
				}
				Integer decimal = null;
				if (clsType == BasicClass.DOUBLE
						|| clsType == BasicClass.FLOAT
						|| clsType == BasicClass.BIGDECIMAL) {
						DecimalNode decimalNode = baseNode.decimal();
						if (decimalNode != null)
								decimal = decimalNode.decimal();
				}
				return new FieldElement(clsType,fieldName,columnName,fieldIndex,pattern,decimal);
		}
	
		protected FieldElement[] getFields() {
				return fields;
		}
		
		public Class getModelClass() {
				return modelClass;
		}
		
		public Validator getValidator() {
				return validator;
		}
		
		public void setValidator(Validator validator) {
			this.validator = validator;
		}

		public Map<String, FieldElement> getFieldEntry() {
				return fieldEntry;
		}
		
		protected boolean equals(String o1,String o2) {
				return o1.hashCode() == o2.hashCode();
		}
		
}
